{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0d0e279f",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-4/parallelization.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239934-lesson-1-parallelization)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4169bfb-769a-4db3-833e-c827f19024b2",
   "metadata": {},
   "source": [
    "# Parallel node execution\n",
    "\n",
    "## Review\n",
    "\n",
    "In module 3, we went in-depth on `human-in-the loop`, showing 3 common use-cases:\n",
    "\n",
    "(1) `Approval` - We can interrupt our agent, surface state to a user, and allow the user to accept an action\n",
    "\n",
    "(2) `Debugging` - We can rewind the graph to reproduce or avoid issues\n",
    "\n",
    "(3) `Editing` - You can modify the state \n",
    "\n",
    "## Goals\n",
    "\n",
    "This module will build on `human-in-the-loop` as well as the `memory` concepts discussed in module 2.\n",
    "\n",
    "We will dive into `multi-agent` workflows, and build up to a multi-agent research assistant that ties together all of the modules from this course.\n",
    "\n",
    "To build this multi-agent research assistant, we'll first discuss a few LangGraph controllability topics.\n",
    "\n",
    "We'll start with [parallelization](https://langchain-ai.github.io/langgraph/how-tos/branching/#how-to-create-branches-for-parallel-node-execution).\n",
    "\n",
    "## Fan out and fan in\n",
    "\n",
    "Let's build a simple linear graph that over-writes the state at each step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "618eab5c-4ef7-4273-8e0b-a9c847897ed7",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U  langgraph tavily-python wikipedia langchain_openai langchain_community langgraph_sdk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "31bbec0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"OPENAI_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1dd77093-1794-4bd7-8c57-58f59a74c20b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "from typing import Any\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "class State(TypedDict):\n",
    "    # The operator.add reducer fn makes this append-only\n",
    "    state: str\n",
    "\n",
    "class ReturnNodeValue:\n",
    "    def __init__(self, node_secret: str):\n",
    "        self._value = node_secret\n",
    "\n",
    "    def __call__(self, state: State) -> Any:\n",
    "        print(f\"Adding {self._value} to {state['state']}\")\n",
    "        return {\"state\": [self._value]}\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"b\", \"c\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdd027d3-ef9f-4d43-b190-e9f07d521e18",
   "metadata": {},
   "source": [
    "We over-write state, as expected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bf260088-90d5-45b2-93ab-42f241560840",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm B\"]\n",
      "Adding I'm D to [\"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm D\"]}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9dec27d-dc43-4088-beb2-53ad090d2971",
   "metadata": {},
   "source": [
    "Now, let's run `b` and `c` in parallel. \n",
    "\n",
    "And then run `d`.\n",
    "\n",
    "We can do this easily with fan-out from `a` to `b` and `c`, and then fan-in to `d`.\n",
    "\n",
    "The the state updates are applied at the end of each step.\n",
    "\n",
    "Let's run it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8fdeaaab-a8c3-470f-89ef-9cf0a2760667",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAGDAHEDASIAAhEBAxEB/8QAHQABAAIDAAMBAAAAAAAAAAAAAAYHBAUIAQMJAv/EAFQQAAEEAQIDAwUIDQgHCQEAAAEAAgMEBQYRBxIhCBMxFCJBUXQVFhc2VmGUswkyNzhCVXF1gZGhstEjM1STsbTS0zlSYneEtcEmRGRygoOFlaLD/8QAGwEBAAMBAQEBAAAAAAAAAAAAAAEDBAIFBgf/xAA2EQACAQIDAgoKAgMAAAAAAAAAAQIDEQQhMRKhBUFRYXGRscHR4RMVIzIzUlOBouIU8EKy8f/aAAwDAQACEQMRAD8A+qaIsLMZaDC0H2pw94BDWRRN5pJXk7NYwelxPQf9ApScnZAzVgWM/i6khZPkqkLx0LZJ2tI/QStMNLzaiHf6imkkjePNxEMvLWiHqeW7GV3r5iW+po8Tn19Gafpx93BgsZBH/qR042j9QCv2aUcpNt82nX5E5Ht99WF/HFD6Sz+Ke+rC/jih9JZ/FPerhfxPQ+jM/gnvVwv4nofRmfwT2PPuJyHvqwv44ofSWfxT31YX8cUPpLP4p71cL+J6H0Zn8E96uF/E9D6Mz+Cex59wyHvqwv44ofSWfxT31YX8cUPpLP4p71cL+J6H0Zn8E96uF/E9D6Mz+Cex59wyP1HqXESvDWZWk9x8GtsMJP7VsWuDmgggg9QR6VqpNI4KZhZJhcc9p8WuqxkH9i17tEQY1xn0/KcHZ3Lu6hG9WUn0SQ+G2/pZyu/2vFLUno2uny8CMiTItXgs0ctHNFYg8iyNV3d2qpdzBjvQ5rthzscOrXbDcdCGuDmt2iplFxdmQERFyAoxPtl+IEVd4DocPTbbDTv/AD07pI2O9W7WRyj/ANxSdRmo3yPiPkQ7m2v4yCSI8vQmGSVsnX8k0XT51fS0k+O3/d1yUSZERUEBVvQ7QuhMzZzlbFZebKWcPXs2bDamPtSMe2A8svdPERbMWuIaREXHcgbKyFzLwrgzWH4pz6f0fhNWYjh9ahyM2TxuqMcYKmNtOkDo3UJj1eyV75HGNrntAPMOUnYATfhv2nNM6z4QV9eZVtvA1mwwOuwy4+25sMkp2ZHE4wg2NyQA6IOBJHrUgq9oHQFzQeT1lHqFg07i521r9mStMySpK5zGhksLmCRh3kZ9s0dHA+HVUPpjM63wnZo0vpDH6d1hp/L6dnoYzUctTFP8q8iD3ssPx7iCJ3bMaeaPmIa/cdfCMZXQuYyOiOOtOhpXWU1LO2sBbxjNQQWLVy9EyWGOZxLy95I7pxLHkPazlJa0eAF5607WGltNSaRkpV8nlKGcy78a+0zD3x3TGQGV0sTRXJnB3jDeTcODnOaXBjtroo3IsjSr2oefuZ42ys7yN0buVw3G7XAOadj4EAj0hVF2i6WRrT8N9R0MNkM5U07qaO9fq4mu6xZbXdVsQmRkTfOfyulZuGgnbc7dFa+Fyjc3iKWQZXs1GWoWTCvdhdDPGHAHlex3VrhvsQfAoDNREQEY1DtidU4DJx7N8qlOMs+Pnscx74z8/LI3YeoSO9fWTqMaxHleS0zRbuZJMk2wdh9qyKN7y4+ocwY38rgpOtFT3IN6272S9EERFnIC0+o8NLkmVbVJ0cWUoSGaq+XcMcS0tdG/bryOaSD47HldsS0LcIuoycHdA0MGQxWs8dfxF6tG90kLq+Qw95rXODHgtc17DuHMcCRuN2uHgSFFo+zfwphka9nDjS7HtIc1zcTACCPAg8qmma01jdQNj8urCSSPfu52PdHLH6+SRpDm/oIWq94z4wWw6kz0LPQ3ytsm36Xscf2q61KWadv7y+RORHx2bOFDSCOG+lgR1BGIg/wqyFF/eTY+VWe/rof8pPeTY+VWe/rof8pPR0/n3MWXKShFCM/pa5jcFkbkOqc4Za9aSVnNLCRzNaSN/wCS+ZQPs23c7xX4H6T1bm9UZZuVylZ0s4quiZEHCV7fNaYyR0aPSno6fz7mLLlLzUDzXAXhvqPK2snldB6dyORtPMk9u1jIZJZXHxc5xbuT85W095Nj5VZ7+uh/yk95Nj5VZ7+uh/yk9HT+fcxZcpHz2bOE58eG+lj/APEQf4VKKVPTnDPTlfH0KlTBYiAubWoUYAxvM5xeWxRMG7nOcXHlaCSSehKx/ePM7o/U+ee30jyiNv7Wxg/tWfidIYvD2jbihfPeIINy5M+xNsfEB7yS0Hp5rdh0HToE2aUdZX6F4+DGR6cHjrFrJTZzJQ9xbmj7mtWJ3NWDcO5XEEgyOcA5+3ToxoLuTmdv0RVTk5u7DdwiIuCAiIgCIiAIiIDUav8AinmvYp/q3KpOxD96rw89ik+vkVt6v+Kea9in+rcqk7EP3qvDz2KT6+RAXkiIgCIiAIiIAiIgCIiAIiIAiIgNRq/4p5r2Kf6tyqTsQ/eq8PPYpPr5Fber/inmvYp/q3KpOxD96rw89ik+vkQF5IiIAiIgCIiAIiIAiIgCIiAIi1Woc/HgasbhC+1bsP7qtVjOzpX7E7b+DQACS4+AB8TsD1GLm9mOoOFPsp/BefJ43AcT6DHyjHRtxGTaOojhdI58EnzASSSNJ9JkYqe+xscDPhD4uv1lka/PhNJ8s8XO3zZbrt+5A9fJsZNwdwWx+tfQ7XWKznEbR+Z01mcRhJMXlar6s8YuzFzWuG27XdyPOadiDt0IBUW7P/C3MdnzhtR0hiK2GuMilksWb0k8rJLUzz1e4CPbo0NaP9ljRufFav4s+VdaJsX4i0WndSyZSeajeqtoZSBjZXwsk72ORjiQHxv5W8w3BBBAIO242c0u3qzThKm9mRAREXACIiAIiIAiIgCIiAKGazP/AGu0qPRvaP6e7H8SpmoXrP436V/4v6tq14X4v2fYyUbFFAuNmqjpLQktiHO2MBfs2YKlOelj237M0z3gNhhgd0fI8bgb9B9segKivZ119qnUt/W2n9WG7Ne0/drshs5OpXq23wzQNlaJo67nRcwPNsWHqC3cA7hXXzsQWjTO3E3HAenEW9/n2mrbf2lTlQap907Gfme59dVU5VeJ1h0d7OnxBERYjkIiIAiIgCIiAIiIAoXrP436V/4v6tql1u3BQqzWbM0detCx0ks0rg1kbANy5xPQAAEklc6aG4k6r486+uaw05jYxwtwQlp4x87OS1nZyeWaeEu2DYmgEN325iNiQebk04aSjUV+R700SixuInDzH8ScLWx9+zdoS1LcV+nfx0ojsVbEZPJIwua5u/Vw2c0ghxBC13D3hHjuHWaz2Wq5bMZXIZwQG/PlrLZnTSRBzWydGjlJa4NIbs0BjQGjbrujrGBp2fis61w8W+41p236WxkH9BXlusaznADGZ3cnbrhLY/8A5rd6GeuyLMyKn3TsZ+Z7n11VTlc98XNT690MMbxIwOB8v0/huaPMYWWPbIWKD9jNNEPwCwsY7kPUhh32HjdOitaYbiJpXG6j09fjyWHyMQmr2Yj0cD4gjxDgQQWnqCCD1Cx4lpySXEvEM3aIiyEBERAEREAREQBEXN3GniBneL2tp+C/Da8+nYDA7V2qIOrcPVd4wRu9NiQbgAfajf08xYBq9bZm92vNdXOH2mrU1ThTg7Aj1Vn6zy33VnaQfc+u8eLB07x4/s5efpnD4ejp7E08ZjKkNDHU4mwV6sDAyOKNo2a1oHgAAtTw+0Bg+F+j8ZpjTlJlDEY+IRQxN6k+lz3H8JziS4uPiSSpEgCIiA8eK5W1JjrPYy1zZ1Zhq80/BjUFoOz2JrsLve/beQBchYPCFx2D2Dw6bfgtHVSxsjjquXx9mjerRXKVmN0M9edgfHKxw2c1zT0IIJBBQDG5KpmMfWv0LMVylaibNBYgeHxyscN2ua4dCCCCCFkrlXTuRs9jHXNbSmYsSzcF9QWi3BZWw8u979t5JNOZ58IXHcsefDrv+E5dUg7jcdQgPKIiAIiIAiIgKL7QvF3P47L4zhhw5iFniTqKAzR2ZG/yGGo8xY+9KdtuhDgwdd3DwJ2a6ccFuDuF4I6JgwGJMlqd7zZyGUs9bGQtO6yTyu6kucfWTsNh6FWUf+kIm/3YD/mq6JQBERAEREAREQGk1pozDcQ9K5LTmoKMeSw+RhMNitKOjgfAg+IcDsQ4dQQCOoVC8GtWZ3gVr2nwW11anyeNtNe7Rep5hv5bXYNzSmd4CaJvgfAt28N2g9KrnbtIfd17PH5/uf3ZAdEoiIAiIgChXGjX97hXwu1Dq3HYJ2pbOIri0cY2z5OZIw5veu7zkftyM53/AGp35NvTupqvTaZBLBJFZEb4ZGlj2S7FrgRsQQfEEID5YN+yK8vaEfxQ+D7fm0wNN+5Xu1/4ryjvu98n/wDTycnz83oX0i4Oa7yHE/hjp7VeTwLtM2svW8rGMdZ8oMUbnHuj3nIzfmj5H/ajbn29C+aEHYrkb21fg+MbnaLjlGcNlzjs7F83N3fNvvzc38hzePNu7bZfV2s2GOFkdcRthjAY1kewa0AdAAPDopswe1ERQAiIgCIiAwszZuU8Penx1NmQyEUEj61SSbuWzyhpLIzJs7kDjsObY7b77HwXy84pfZC5tZ8RNA5uxw5fibGi8lPakoyZgvdYc6PuzGSa7TGQeu+zvVsF9T3ODQSSAB6Svl123uyvfn7SuDn0nXa6pxAtAAtG8da7uO/c8gdGkHviT65PQ1TYHdPZf482u0bw1k1fY0u/SsRvy1K1d9zyoWI2NZvM1/ds6c7ns22PWM9fQLdUa4caKxPDbQ2E0rhGtbjsRVZWj5dt3bDq9234TnczifSSVJVACIiAxcpd9zcZbt8vN3EL5eX18rSf+irzF6SxWex1TJZnH1MxkrULJprN6Bszt3AEtbzDzWDwDRsNh69ypzqr4sZj2Ob9wqPaa+LmK9ki/cC9LDtwpuUXZ3OtEYXwfaW+TWH+gRf4ViZbS2L09jLmUwuPqYfJVIHzRWKULYSS1pIa/lHnMPgWnfx6bHYiueE3ai07q2pQx+o8pUxeqLmTu46KtHVnirPfHaljijEzwY+9dGxh5Ofcl3QDcBW3qv4rZj2Ob9wrRTrTnJJybXSE3cmGPti/QrWQ3lE0TZA31bgH/qsha7TnxexfssX7gWxXjzVpNI5CIi5AREQFcOx9TWuVys+YrRZGGpcfUrVbLBJDE1gALgwjbmJ3JcdztsBsF7fg+0t8msP9Ai/wrzpT+e1B+d7X7y0+uONGjeHOTgxuezBr5GaE2G061Wa1M2IHbvHMhY8sZvuOZwA6Hr0XtzqSpvZjKyXOdNtG3OgsBFs+liqmLtM6xW8fAyGaJ3ra5o+YdDuDtsQR0Uo0ZmJc/pTFZCxy+UWK7HS8nRpfts4j5twdlgUrkORpwW67xLXnjbLG8Dbma4bg9fmK8cLfue4L2cf2lZ67c6W1J3aa3p+A1WZKkRF5hyavVXxYzHsc37hUe018XMV7JF+4FJNRwvsaeykUbS6R9WVrWj0ksICjWl3tk01iXNO7XVISD6xyBehR+C+nuJ4jkyvi9Qag4IN4Us0TqOvqK3qCawzJ28a6KjThOWfaFryh3mgiM9GjzyTttsdz1jqv4rZj2Ob9wrarU6tc1mlM05x5WilMSfUO7KtpK04/YLUlGnPi9i/ZYv3AtisHBQvr4THxSNLZI68bXNPoIaAQs5ebP3mGERFwQEREBX2lP57UH53tfvKo5slkeEfHPXudyGlM/n8VqiDHyUL+Ax7rxhNeExPrSNZ50e7vPaSOU856ghW9phhis6hY7o4ZawSPVzcrh+wg/pW8Xr1lef8AeQl6mPj7Zv0K1l1eao6aJshgsACSPcA8rgCQHDfY7E9QvPC37nuC9nH9pXv8F6uGEbo+H2BDgRzVWPG423B6g/pBCpq/BfSuxjiJQiIvOICidrh+3v5H4zN5LBwvcXuq0xA+EOPUlrZYn8u567NIG5J26qWIrIVJU/dZN7HPLMvqV3akfw199V/3Ebo4ah8p8mqeU+UeW9xyc3c8vJy9duXff07dFbdTQDBPG/J5nI5uKNwe2tcEDYuYHcFzYomc2x6gO3G4B23G6qGP/SETf7sB/wA1XRKueJqta7ku4XYREWUgIiIAiIgI/m9Gw5a4bta9bw95zQySxRMe8oHgHtkY9rtvQdtx4b7KmeLOX1LoPiRws09Q1Vfmp6qyc9K5JYrVDJGxkPODGRCADv47g9PQuhlzt2kPu69nj8/3P7stEcRUirJ9aT7UTctqPh4ZvMyWocrlqh+3qTivHHKP9V/dRMcW+sb7EEggg7KWsY2NjWMaGtaNg0DYAepfpFxOrOp7z7uwXuERFUQEREBztH/pCJv92A/5quiVzJxsyVrgb2j8FxjylN9zQlzAN0llrdYF0mJc6330dmRoHnRFxawkeHX0lod0rSu18jTgt1J47VWeNssU8Lw9kjHDdrmuHQgggghAe5ERAEREAREQBc7dpD7uvZ4/P9z+7Lolcv5zOu7Qvac0nW0pCJ9NcM7s9nM6gc7eCW5JFyCnBt9u9vi477Dr6hzAdQIiIAiIgCIiAw8viKWoMVcxmSqxXsfcidBYrTsD45Y3DZzXA+IIJC5i0jmbvY51xU0NqO1Na4Q5yyWaZztl5d7jWHEnyCw8+EZ6ljz4dd+nNydUrQa70LhOJeksnprUVGPI4fIRGKeB/wCsOafFrmkAhw6ggEIDfouKZO03b7FlfNcOeIzrOq5sXQbc0hkIHjvspUc8xxV7J6905ha4GQjYtjdsHEND7w7JHHW32huDNLVOThpVsy21PTvwY9r2wRyMdu0ND3OcN43xE7uPUn0IC5kREARRLi3rtnDHhhqnVb2secRjp7cccu/LJI1h7th2/wBZ/K39K460T9kG1Fx205T0RpLTDMRxczEraNey6YPx0Mfcl890c3nNLAyRwhIfsOU80nVqAuXjdxJz/EnWj+DHDG4auZkja/U+pourMFTd4saR/wB4kHRo33AO/Tq5ly8NOG2B4SaKxmltN0xTxVCPlYD1fI49XSPd+E9x3JPrPoGwWm4I8GMPwQ0YzC46SS/fsSG3lMxa62Mjad1kmkcdzuT4Dc7D0k7k2CgCIiAIiIAiIgCIiA+a3ac+x48Ss3qrN6zwGpncRLN+d9iaHJuZXyAHg1gPSJ4awBoDe7AAAbGAABI/sc+U1Nwlsa/0NqjTmYx9zvKt6nRsU3xEyuEkb93OAaA5sbHBxOxbE4jfZfQZQvVjj7+tNN6Fvkl52xHpBgG/6if1rRQgqlS0tM31JslB2qNVk7s05iuUj8PMyA/pArEftRup9WFw5tO4gN36kZqUn+6rYrDgzOPs5S1jYb1aXI1WMlsU2TNdNCx/NyOewHdodyu2JHXlO3gVttS+mvy8RfmOae37rzM5bgQ3R2PwN92otQZWrTdQpxus99EA+feJzG+f50DW8uwd0O7diC7mLgd9jr4saouY/O5O4OG8cEzLFezO5xyMbmuDmyRxMILHNI3HO5jgQF9KY3FvEjBgbDmxt7c7df5yr6VOVjrwjBpxyTV97XcGY2Nqy0cdVrTXJshNDE2N9yyGCWdwABkeI2tYHOI3PK1rdz0AHRZKIsxAREQBERAEREAREQBQrVnx90z7Hf8A3q6mqhWrPj7pn2O/+9XWvC/F+0v9WSiD8dNa5rSuJ05jNOTQUs3qbNQYSvkLMXex0g9kkj5uQ7B7gyJ/K0nYuI36bqHcGMNmMD2heJ9TN6gl1NcbiMIRfmqRVpHNJubNc2IBm4O/UAdCPVubS4kcN8TxR08zE5Z9qv3NmO7Uu0Ju5s07EZ3jmifseV43PXY9CRt1Wq4e8H6XD7UWbzoz+d1BlsxXrVrVnN2I5XFsHed3yhkbA3+dduB06DoDuTbZ3uQSdn3ScF+bb/1lVTpQVn3ScF+bb/1lVTpcYn/Do72S+IIiLEQEREAREQBERAEREAUW1jjLJvYvM1YH2zQEsc1eIAyOikDeZzB+EWljDy+JHNtudmmUorKc3TltIlZFdTa+xFeNz5vdCFrG8zzJi7TeUbbnfePpt6d/BY+I4oacz9CC9jLk+QpTjeKzVo2JIpBvtuHhmxG4PpU21f8AFPNexT/VuVSdiH71Xh57FJ9fItf8in8r6/IZE/07VmzepIc2a09SjVqS1oBaidFJM6V0bnO5HAOa1oiAHNsSXO6bBpMzRFlq1PSO/EgwiIqiAiIgCIiAIiIAiIgCIiA1Gr/inmvYp/q3KpOxD96rw89ik+vkVt6v+Kea9in+rcqk7EP3qvDz2KT6+RAXkiIgCIiAIiIAiIgCIiAIiIAiIgNRq/4p5r2Kf6tyqTsQ/eq8PPYpPr5FBO2F2ypeztqCvpWTREudrZnEOnjynuj5Mxr3PkjdGG9y/mLQ1jj5w+3HQeJpvsUdtG9LW4e8Gsbw/fkbDHGrLlhluQRw875JZjF3B+0YXHl5+vLtuN0B9EUREAREQBERAEREAREQBFXXEXig7AWH4jDCKfKtA7+aXd0dQEAt3A+2eQQQ3cbAgnxaHU9kpbeckdJlcjdycjjufKJ3cm/zRt2Y38jWhe7hOCauJiqknsp6cvUTktTqZFyV7gY7+iR/qT3Ax39Ej/UvR9Qr6v4/sRdEj7evAocZ+B161RrCbUmmw/JUC0ee+MD+XhHQnzmDcAeLo2BVJ9i94F+9/SOT4m5OAtvZrmo4wPb1ZUY4d5IP/PI3b8kXqcpx7gY7+iR/qT3Ax39Ej/UnqFfV/H9hdHWqLkr3Ax39Ej/UnuBjv6JH+pPUK+r+P7C6OtUXKlOu7GPElCzbx0g22fUsviP7DsR8x6KztB8WbUduHG6kmZNHKRHBk+QMPOTsGygeb136PAA9BA8TixPA1WjFzpy2kuaz7ycnoW6iIvniAiIgC1Wqs2NNaaymVLBIadaSZsZO3O4NJa39J2H6VtVFOK1OS9w51DHEC57aj5Q0dSeTzyP0hqvoRjOrCMtG12krUoGAS8hfYlM9mRxkmmd4ySOO7nH8pJX7Xhj2yMa9pDmuG4I9IXlfqOhwwtRqfVuJ0bj23cxcbTgfIIo/MdI+R58GsY0Fz3dCdmgnoVt1U3G/Tly7ndG51lPL5DFYqayy9XwM8sVxrZo2tZLH3TmvdyluxDTvs89D1VNacoQcorP+9moJT8L+kBgm5h2ajZjzaFEyPika6OcgkRvYW8zHEDwcB4j1hZeL4k6bzGJyeSgybIqmLJF51uJ9Z9bzeb+UZI1rm7g7jcdfQqtt6Sqz0MDk8Fg9SRS2dWY+e47NmxNZfHDuBM4Suc9jADtu7bbbqB0Xq4i6NzeYzvEeWlibF2FzsFcZXMZazItryPfNExx81x5QBt6+UHxCxOvWSvZPovyN9wJppXjFS1txK9w8NKyziW4d198stWaGYS981jQBIG7sLXbg8vX0FWQqn0vlrOrONceajwObxeNZpx9UzZXHvrAymyx3IOb07dfn2O24VsLVh5SnFuTvmQF+ZY2zRvjeOZjwWuB9IK/SLSC9OEmoJtQaJqutSGW5Te+lNI47l5YdmuJ9bmcjj85KmSrbgNVfFpLIWXfzdvJTSRn1hrWRH/8AUTlZK/NcdGMMTUjHS7LHqERFhIC8OaHtLXAOaRsQRuCF5RAc5ay0dNoPLeTFjnYiw8+Q2epaAdz3Dj6HNHQb/bNAIJIcBBs/w80xqq625mdP43K22sETZ7lVkrwwEkN3cCdtyenzldfX8fVytOWpdrxW6szeWSCdgex49RB6FV3keBGImkc7HZPI4pp8IWPbNG38gkaXD8nMvr8NwvSnBQxSzXHa6fmLXOcvgW0D8jMF/wDXxf4VvdPaSwukoJYcJiaeJhldzyR04GxNe7bbchoG52Vw/AG75T2/osSfAG75T2/osS3R4RwEXeLS+z8Bs85WiKy/gDd8p7f0WJPgDd8p7f0WJWetsH8+5+A2ecqbN4HG6lx76GWoV8lSeQ51e1EJGEg7glp6dCo2ODOgmncaNwYO23ShF/hV+/AG75T2/osSfAG75T2/osS4lwlgJO8pX+z8Bs85ReM4V6Nwt+C9j9LYilcgdzRWIKUbHsPrBA3CmmEwl3VWWGLxm3lBAdNMRu2rGTt3jvn8eVvi4j1BxFlVOAtFr2m7ncnbYCCYo+7ha75iWt5v1EKwMFp/HaZx7aOLqR06zTzckY6ud6XOJ6ucdhuSSSsdfhehSg44ZXfRZeYskfvB4atp7D08ZTaWVakTYYw47uIA23J9JPiT6SSVnIi+Obcm5PVgIiKAEREAREQBERAEREAREQBERAEREAREQH//2Q==",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35238fde-0230-4ae8-9200-158a8835c4f1",
   "metadata": {},
   "source": [
    "**We see an error**! \n",
    "\n",
    "This is because both `b` and `c` are writing to the same state key / channel in the same step. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9048b041-6849-4f09-9811-6b7a80f67859",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "An error occurred: At key 'state': Can receive only one value per step. Use an Annotated key to handle multiple values.\n"
     ]
    }
   ],
   "source": [
    "from langgraph.errors import InvalidUpdateError\n",
    "try:\n",
    "    graph.invoke({\"state\": []})\n",
    "except InvalidUpdateError as e:\n",
    "    print(f\"An error occurred: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64cc329d-59fa-4c26-adcf-9122a824955d",
   "metadata": {},
   "source": [
    "When using fan out, we need to be sure that we are using a reducer if steps are writing to the same the channel / key. \n",
    "\n",
    "As we touched on in Module 2, `operator.add` is a function from Python's built-in operator module.\n",
    "\n",
    "When `operator.add` is applied to lists, it performs list concatenation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8f1292ac-510a-4801-b2a3-e2c6d2d9582a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import operator\n",
    "from typing import Annotated\n",
    "\n",
    "class State(TypedDict):\n",
    "    # The operator.add reducer fn makes this append-only\n",
    "    state: Annotated[list, operator.add]\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"d\")\n",
    "builder.add_edge(\"c\", \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ffbad231-fc1d-49b1-a9fc-ed9153fa3977",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\"]}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bdf5baa2-cecd-44b6-b0c4-d258340783f8",
   "metadata": {},
   "source": [
    "Now we see that we append to state for the updates made in parallel by `b` and `c`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed6fc7c7-198d-41be-867f-e77c93ba3217",
   "metadata": {},
   "source": [
    "## Waiting for nodes to finish\n",
    "\n",
    "Now, lets consider a case where one parallel path has more steps than the other one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f50b5d4f-dd39-4c22-b623-e0abc23f9144",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"b2\")\n",
    "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11640e6f-ac62-4ad4-89d9-7f6f9b56bf7a",
   "metadata": {},
   "source": [
    "In this case, `b`, `b2`, and `c` are all part of the same step.\n",
    "\n",
    "The graph will wait for all of these to be completed before proceeding to step `d`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fafda930-e75b-410f-ba93-eb5fc0219303",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\", \"I'm D\"]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6610a2e3-b053-47e8-bf4e-0968dfaa0a5d",
   "metadata": {},
   "source": [
    "## Setting the order of state updates\n",
    "\n",
    "However, within each step we don't have specific control over the order of the state updates!\n",
    "\n",
    "In simple terms, it is a deterministic order determined by LangGraph based upon graph topology that **we do not control**. \n",
    "\n",
    "Above, we see that `c` is added before `b2`.\n",
    "\n",
    "However, we can use a custom reducer to customize this e.g., sort state updates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "24788e73-0950-432e-ad32-7987ea076529",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def sorting_reducer(left, right):\n",
    "    \"\"\" Combines and sorts the values in a list\"\"\"\n",
    "    if not isinstance(left, list):\n",
    "        left = [left]\n",
    "\n",
    "    if not isinstance(right, list):\n",
    "        right = [right]\n",
    "    \n",
    "    return sorted(left + right, reverse=False)\n",
    "\n",
    "class State(TypedDict):\n",
    "    # sorting_reducer will sort the values in state\n",
    "    state: Annotated[list, sorting_reducer]\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
    "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
    "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
    "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
    "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"a\")\n",
    "builder.add_edge(\"a\", \"b\")\n",
    "builder.add_edge(\"a\", \"c\")\n",
    "builder.add_edge(\"b\", \"b2\")\n",
    "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
    "builder.add_edge(\"d\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "607dba2e-f9f0-4bc7-8ba6-684521a49bdc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Adding I'm A to []\n",
      "Adding I'm C to [\"I'm A\"]\n",
      "Adding I'm B to [\"I'm A\"]\n",
      "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
      "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\"]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'state': [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\", \"I'm D\"]}"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.invoke({\"state\": []})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb1714c0-e881-48e7-bcb8-a60016f0485e",
   "metadata": {},
   "source": [
    "Now, the reducer sorts the updated state values!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34e0750b-e6af-40d9-835c-c664da5a2d3b",
   "metadata": {},
   "source": [
    "## Working with LLMs\n",
    "\n",
    "Now, lets add a realistic example! \n",
    "\n",
    "We want to gather context from two external sources (Wikipedia and Web-Seach) and have an LLM answer a question."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e1e9d03c-cb41-415c-862d-c9616d5a2d07",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0f75cc78-d1a1-47a5-8648-bf5a79c883de",
   "metadata": {},
   "outputs": [],
   "source": [
    "class State(TypedDict):\n",
    "    question: str\n",
    "    answer: str\n",
    "    context: Annotated[list, operator.add]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e714ea8-095c-461a-98bc-ee782a84ef5c",
   "metadata": {},
   "source": [
    "You can try different web search tools. [Tavily](https://tavily.com/) is one nice option to consider, but ensure your `TAVILY_API_KEY` is set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c8bb519a-d08a-4ec7-8f0b-2ce6a9bf7342",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "_set_env(\"TAVILY_API_KEY\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "bfb4f56c-3334-4927-8ed8-62fd384ee43e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage, SystemMessage\n",
    "\n",
    "from langchain_community.document_loaders import WikipediaLoader\n",
    "from langchain_community.tools import TavilySearchResults\n",
    "\n",
    "def search_web(state):\n",
    "    \n",
    "    \"\"\" Retrieve docs from web search \"\"\"\n",
    "\n",
    "    # Search\n",
    "    tavily_search = TavilySearchResults(max_results=3)\n",
    "    search_docs = tavily_search.invoke(state['question'])\n",
    "\n",
    "     # Format\n",
    "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
    "        [\n",
    "            f'<Document href=\"{doc[\"url\"]}\"/>\\n{doc[\"content\"]}\\n</Document>'\n",
    "            for doc in search_docs\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    return {\"context\": [formatted_search_docs]} \n",
    "\n",
    "def search_wikipedia(state):\n",
    "    \n",
    "    \"\"\" Retrieve docs from wikipedia \"\"\"\n",
    "\n",
    "    # Search\n",
    "    search_docs = WikipediaLoader(query=state['question'], \n",
    "                                  load_max_docs=2).load()\n",
    "\n",
    "     # Format\n",
    "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
    "        [\n",
    "            f'<Document source=\"{doc.metadata[\"source\"]}\" page=\"{doc.metadata.get(\"page\", \"\")}\"/>\\n{doc.page_content}\\n</Document>'\n",
    "            for doc in search_docs\n",
    "        ]\n",
    "    )\n",
    "\n",
    "    return {\"context\": [formatted_search_docs]} \n",
    "\n",
    "def generate_answer(state):\n",
    "    \n",
    "    \"\"\" Node to answer a question \"\"\"\n",
    "\n",
    "    # Get state\n",
    "    context = state[\"context\"]\n",
    "    question = state[\"question\"]\n",
    "\n",
    "    # Template\n",
    "    answer_template = \"\"\"Answer the question {question} using this context: {context}\"\"\"\n",
    "    answer_instructions = answer_template.format(question=question, \n",
    "                                                       context=context)    \n",
    "    \n",
    "    # Answer\n",
    "    answer = llm.invoke([SystemMessage(content=answer_instructions)]+[HumanMessage(content=f\"Answer the question.\")])\n",
    "      \n",
    "    # Append it to state\n",
    "    return {\"answer\": answer}\n",
    "\n",
    "# Add nodes\n",
    "builder = StateGraph(State)\n",
    "\n",
    "# Initialize each node with node_secret \n",
    "builder.add_node(\"search_web\",search_web)\n",
    "builder.add_node(\"search_wikipedia\", search_wikipedia)\n",
    "builder.add_node(\"generate_answer\", generate_answer)\n",
    "\n",
    "# Flow\n",
    "builder.add_edge(START, \"search_wikipedia\")\n",
    "builder.add_edge(START, \"search_web\")\n",
    "builder.add_edge(\"search_wikipedia\", \"generate_answer\")\n",
    "builder.add_edge(\"search_web\", \"generate_answer\")\n",
    "builder.add_edge(\"generate_answer\", END)\n",
    "graph = builder.compile()\n",
    "\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "fa544ca0-10af-491e-ad7a-477d004413eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Nvidia's Q2 2024 earnings were notably strong. The company reported a GAAP net income of $6.188 billion, a significant increase from $656 million in the same quarter the previous year. Revenue for the quarter was $13.507 billion, up from $6.704 billion in Q2 2023. The GAAP gross profit was $9.462 billion with a gross margin of 70.1%. Non-GAAP gross profit was $9.614 billion with a gross margin of 71.2%. Operating income also saw a substantial rise to $6.800 billion from $499 million in the previous year. Overall, Nvidia demonstrated robust financial performance in Q2 2024.\""
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = graph.invoke({\"question\": \"How were Nvidia's Q2 2024 earnings\"})\n",
    "result['answer'].content"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "3dbbecab-80eb-4f0c-b43a-45542fc0ae9c",
   "metadata": {},
   "source": [
    "## Using with LangGraph API\n",
    "\n",
    "--\n",
    "\n",
    "**⚠️ DISCLAIMER**\n",
    "\n",
    "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
    "\n",
    "*Also, if you are running this notebook in CoLab, then skip this step.*\n",
    "\n",
    "--\n",
    "\n",
    "Let's load our the above graph in the Studio UI, which uses `module-4/studio/parallelization.py` set in `module-4/studio/langgraph.json`.\n",
    "\n",
    "![Screenshot 2024-08-29 at 3.05.13 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbb10f43c3d4df239e0278_parallelization-1.png)\n",
    "\n",
    "Let's get the URL for the local deployment from Studio."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4bc8ad8d-1365-4801-a8a5-b85cd4965119",
   "metadata": {},
   "outputs": [],
   "source": [
    "import platform\n",
    "\n",
    "if 'google.colab' in str(get_ipython()) or platform.system() != 'Darwin':\n",
    "    raise Exception(\"Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "23919dc9-27d8-4d10-b91d-24acdf8c0fb9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langgraph_sdk import get_client\n",
    "client = get_client(url=\"http://localhost:63082\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "ff35e68f-4017-4f45-93cf-ddbb355a0bc1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Nvidia's Q2 2024 earnings were exceptionally strong. The company reported $13.5 billion in revenue, significantly surpassing expectations, and made $6 billion in pure profit. The earnings per share were $2.70, adjusted, compared to the $2.09 per share expected by analysts. The gross profit margins were 75.1%, and the adjusted earnings per share were 68 cents. The strong performance was driven by unprecedented demand for its generative AI chips.\n"
     ]
    }
   ],
   "source": [
    "thread = await client.threads.create()\n",
    "input_question = {\"question\": \"How were Nvidia Q2 2024 earnings?\"}\n",
    "async for event in client.runs.stream(thread[\"thread_id\"], \n",
    "                                      assistant_id=\"parallelization\", \n",
    "                                      input=input_question, \n",
    "                                      stream_mode=\"values\"):\n",
    "    # Check if answer has been added to state  \n",
    "    answer = event.data.get('answer', None)\n",
    "    if answer:\n",
    "        print(answer['content'])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
